//
//  TestPhysics.cpp
//  NanoOSX
//
//  Created by Jeppe Nielsen on 9/8/13.
//  Copyright (c) 2013 Jeppe Nielsen. All rights reserved.
//

#include "Engine.hpp"
#include "OpenGL.hpp"
#include "GameWorld.hpp"
#include "RenderSystem.hpp"
#include "Light.hpp"
#include "FirstPersonMoverSystem.hpp"
#include "PhysicsSystem.hpp"

using namespace Nano;

class TestPhysics : public GameState<TestPhysics> {
    
    GameWorld world;
    RenderSystem* renderer;
    GameObject* cube;
    float rotation;
    GameObject* cubeMesh;
    GameObject* grassTexture;
    GameObject* camera;
    float timer;
    
    void Initialize() {
        renderer = world.CreateSystem<RenderSystem>();
        world.CreateSystem<FirstPersonMoverSystem>()->Input = &Input;
        world.CreateSystem<PhysicsSystem>()->Gravity = Vector3(0,-10,0);
        
        grassTexture = world.CreateObject();
        grassTexture->AddComponent<TextureComponent>()->Texture().LoadFromPng("grass.png");
        
        cubeMesh = world.CreateObject();
        cubeMesh->AddComponent<Mesh>()->AddCube(0, 1);
        
        camera = world.CreateObject();
        camera->AddComponent<Transform>()->Position = Vector3(0,0,4);
        camera->AddComponent<Camera>()->Viewport = Manager().Viewport();
        camera->AddComponent<FirstPersonMover>();
        
        GameObject* light = world.CreateObject();
        light->AddComponent<Transform>()->Rotation = Quaternion::LookAt(Vector3(1,1,1), 0, Vector3(0,1,0));
        light->AddComponent<Light>();
        
        GameObject* ambientLight = world.CreateObject();
        ambientLight->AddComponent<Light>()->Type = Nano::Light::LightType::Point;
        ambientLight->AddComponent<Colorable>()->Color = Colour::White() * 0.25f;
        
        cube = world.CreateObject();
        cube->AddComponent<Transform>();
        cube->AddComponent<Mesh>()->AddCube(0, 1);
        cube->AddComponent<TextureComponent>(grassTexture);
        cube->AddComponent<Material>()->LightingEnabled = true;
        
        
        for (int y=0; y<20; y++) {
            for(int x=0; x<((y & 1) ? 4 : 5); x++) {
                CreateCube(Vector3(x*2.5f+((y & 1) ? 1.25f : 0),1+y*2.005f,-10));
            }
        }
        
        GameObject* floor = world.CreateObject();
        floor->AddComponent<Transform>()->Position = Vector3(0,-1,0);
        floor->AddComponent<Mesh>()->AddPlane(0, 10, Box(0,0,10,10));
        floor->AddComponent<TextureComponent>(grassTexture);
        floor->AddComponent<Material>()->LightingEnabled = false;
        
        
        
        btCollisionShape* groundShape = new btBoxShape(btVector3(100,0.1f,100));
        btRigidBody::btRigidBodyConstructionInfo* groundDefinition = new btRigidBody::btRigidBodyConstructionInfo(0,0,groundShape,btVector3(0,0,0));
        
        floor->AddComponent<RigidBody>()->Body = groundDefinition;
        
        rotation = 0;
        timer = 0;
        lastTimer = 0;
        
        Input.TouchDown += event_handler(this, &TestPhysics::TouchDown);
    }
    
    GameObject* CreateCube(Vector3 position) {
        
        GameObject* cube = world.CreateObject();
        cube->AddComponent<Transform>()->Position = position;
        cube->AddComponent<Mesh>(cubeMesh);
        cube->AddComponent<Material>()->LightingEnabled = false;
        cube->AddComponent<TextureComponent>(grassTexture);
        
        btCollisionShape* boxShape = new btBoxShape(btVector3(1,1,1));
        btVector3 boxIn(1,1,1);
        boxShape->calculateLocalInertia(1, boxIn);
        
        btRigidBody::btRigidBodyConstructionInfo* info = new btRigidBody::btRigidBodyConstructionInfo(1,0,boxShape,btVector3(1,1,1));
        
        cube->AddComponent<RigidBody>()->Body = info;
        
        return cube;
    }
    
    
    void Update(float dt) {
        rotation += dt;
        timer += dt;
        world.Update(dt);
        cube->GetComponent<Transform>()->Rotation = Quaternion(rotation, Vector3(1,1,0).Normalized());
    }
    
    void Render() {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glEnable(GL_CULL_FACE);
        glEnable(GL_DEPTH);
        renderer->Render();
    }
    
    float lastTimer;
    
    void TouchDown(TouchEvent e) {
        
        bool allowed = (timer - lastTimer)<0.3f;
        lastTimer = timer;
        if (!allowed) return;
        
        Matrix4x4* camWorld = camera->GetComponent<Transform>()->World.GetValue();
        Vector3 pos = camWorld->TransformPosition(Vector3(0,0,-2));
        
        GameObject* cube = CreateCube(pos);
        cube->GetComponent<RigidBody>()->ApplyImpulse(camWorld->TransformVector(Vector3(0,0,-20)), 0);
    }    
};

/*
int main() {
    Engine e;
    e.Start<TestPhysics>();
}
*/

